package com.ydcycloud.log.syslog;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.smart.util.StringUtils;
import com.ydcycloud.log.model.SysLog;
import com.ydcycloud.log.service.SysLogService;
import com.ydcycloud.sys.annotation.LogAnnotation;
import com.ydcycloud.sys.model.SessionAdmin;

@Aspect
@Service("logAspect")
public class LogAspect {

	// 本地异常日志记录对象
	private static final Logger logger = LoggerFactory
			.getLogger(LogAspect.class);
	@Resource
	private SysLogService sysLogService;

	// Controller层切点
	@Pointcut("@annotation(com.ydcycloud.sys.annotation.LogAnnotation)")
	public void controllerAspect() {

	}

	/**
	 * 前置通知 用于拦截Controller层记录用户的操作
	 * 
	 * @param joinPoint
	 *            切点
	 */
	@After("controllerAspect()")
	public void doBefore(JoinPoint joinPoint) {

		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
				.getRequestAttributes()).getRequest();
		HttpSession session = request.getSession();
		// 读取session中的用户
		SessionAdmin sessionAdmin = (SessionAdmin) (session
				.getAttribute("sessionAdmin") == null ? new SessionAdmin(1,
				"未知") : session.getAttribute("sessionAdmin"));
		SysLog sysLog = new SysLog();
		// 请求的IP
		String ip = request.getRemoteAddr();

		try {
			// 获取用户请求方法的参数并序列化为JSON格式字符串
			LogAnnotation logAnnotation = getControllerMethodDescription(joinPoint);
			if (logAnnotation != null // 存在这个注解
					&& StringUtils.isNotBlank(logAnnotation.params()) // 需要入库的参数
					&& joinPoint.getArgs() != null // 参数部位空
					&& joinPoint.getArgs().length > 0) {
				int index = 0;
				String param = logAnnotation.params(); // 获取需要入库的参数

				String classType = joinPoint.getTarget().getClass().getName();
				Class<?> clazz = Class.forName(classType);
				String[] paramNames = getFieldsName(this.getClass(),
						clazz.getName(), joinPoint.getSignature().getName());
				for (int i = 0; i < paramNames.length; i++) {
					if (param.equals(paramNames[i])) {
						index = i;
						break;
					}

				}

				sysLog.setParamValue(joinPoint.getArgs()[index] == null ? "" : joinPoint.getArgs()[index].toString());
				sysLog.setParamName(logAnnotation.value());
			}

			sysLog.setCreateBy(sessionAdmin.getName());
			sysLog.setLogContent(logAnnotation.log());
			sysLog.setManagerId(sessionAdmin.getId());
			sysLog.setOperateIp(ip);
			sysLog.setStatus(0);
			sysLog.setType(0);
			sysLog.setType(logAnnotation.type().getValue());
			// *========数据库日志=========
			sysLogService.save(sysLog);
			System.out.println("=====前置通知结束=====");
		} catch (Exception e) {
			// 记录本地异常日志
			e.printStackTrace();
			logger.error("==前置通知异常==");
			logger.error("异常信息:{}", e.getMessage());
		}
	}

	@SuppressWarnings("rawtypes")
	private static String[] getFieldsName(Class cls, String clazzName,
			String methodName) throws NotFoundException {
		ClassPool pool = ClassPool.getDefault();
		// ClassClassPath classPath = new ClassClassPath(this.getClass());
		ClassClassPath classPath = new ClassClassPath(cls);
		pool.insertClassPath(classPath);

		CtClass cc = pool.get(clazzName);
		CtMethod cm = cc.getDeclaredMethod(methodName);
		MethodInfo methodInfo = cm.getMethodInfo();
		CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
		LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute
				.getAttribute(LocalVariableAttribute.tag);
		if (attr == null) {
			// exception
		}
		String[] paramNames = new String[cm.getParameterTypes().length];
		int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
		for (int i = 0; i < paramNames.length; i++) {
			paramNames[i] = attr.variableName(i + pos); // paramNames即参数名
		}
		return paramNames;
	}

	/**
	 * 异常通知 用于拦截service层记录异常日志
	 * 
	 * @param joinPoint
	 * @param e
	 */

	@AfterThrowing(pointcut = "controllerAspect()", throwing = "e")
	public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
				.getRequestAttributes()).getRequest();
		HttpSession session = request.getSession();
		// 读取session中的用户
		SessionAdmin sessionAdmin = (SessionAdmin) session
				.getAttribute("sessionAdmin");
		// 获取请求ip
		String ip = request.getRemoteAddr();
		// 获取用户请求方法的参数并序列化为JSON格式字符串
		String params = "";
		/*
		 * if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
		 * for ( int i = 0; i < joinPoint.getArgs().length; i++) { params +=
		 * JSONUtils.toJSONString(joinPoint.getArgs()[i]) + ";"; } }
		 */
		try {
			/* ========控制台输出========= */
			System.out.println("=====异常通知开始=====");
			System.out.println("异常代码:" + e.getClass().getName());
			System.out.println("异常信息:" + e.getMessage());
			System.out.println("异常方法:"
					+ (joinPoint.getTarget().getClass().getName() + "."
							+ joinPoint.getSignature().getName() + "()"));
			System.out.println("方法描述:"
					+ getControllerMethodDescription(joinPoint));
			System.out.println("请求人:" + sessionAdmin.getName());
			System.out.println("请求参数:" + params);
			System.out.println("请求IP:" + ip);

		} catch (Exception ex) {
			// 记录本地异常日志
			logger.error("==异常通知异常==");
			logger.error("异常信息:{}", ex.getMessage());
		}
		/* ==========记录本地异常日志========== */
		logger.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget()
				.getClass().getName()
				+ joinPoint.getSignature().getName(), e.getClass().getName(),
				e.getMessage(), params);

	}

	@SuppressWarnings("rawtypes")
	public static LogAnnotation getControllerMethodDescription(
			JoinPoint joinPoint) throws Exception {
		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();
		Class targetClass = Class.forName(targetName);
		Method[] methods = targetClass.getMethods();
		LogAnnotation logAnnotation = null;
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				Class[] clazzs = method.getParameterTypes();
				if (clazzs.length == arguments.length) {
					logAnnotation = method.getAnnotation(LogAnnotation.class);
					break;
				}
			}
		}
		return logAnnotation;
	}

	/**
	 * 环绕通知
	 * 
	 * @Description: TODO
	 * @param proceedingJoinPoint
	 * @return Object
	 * @throws Throwable
	 * @throws
	 * @author hdt
	 * @date 下午2:11:01
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint)
			throws Throwable {

		MethodSignature classMethod = (MethodSignature) proceedingJoinPoint
				.getSignature();
		Method method = classMethod.getMethod();

		Method realMethod = proceedingJoinPoint
				.getTarget()
				.getClass()
				.getDeclaredMethod(classMethod.getName(),
						method.getParameterTypes());

		Class c = realMethod.getClass();

		if (c.isAnnotationPresent(LogAnnotation.class)) {
			LogAnnotation l = (LogAnnotation) c
					.getAnnotation(LogAnnotation.class);
			System.out.println(l.log());
		}

		System.out
				.println(method + "====" + classMethod.getName() + "----" + c);
		Object r = null;
		r = proceedingJoinPoint.proceed();
		System.out.println(r);
		return r;

	}
}
